終於到了最後一個Design Principle了。若對其他Design Principle不熟悉的,可以先看前面的文章喔!!
各單元對其他單元所知應當有限:只瞭解與目前單元最相關之單元
白話文年糕:別太好奇,跟你朋友聊天就好。
迪米特法則,又稱最少知識原則。就是一個類別,對自己需要呼叫或耦合的類別不需要知道的太多,只需要知道該類別提供的public方法,不需要知道內部的實作。
在使用LoF時,除了知道了定義以外,還要了解LoF中的四層含義
LoD有一段英文的解釋,Only talk to your immediate friends; 只與直接的朋友講話
。什麼是朋友?兩個物件只要有耦合,就會成為朋友。好比說:合成、聚合及依賴。
例如:老師要班長清點班上人數,就清點這件事來說,老師只需要跟班長有耦合,不需要跟班級有耦合。
class Clazz {
public void getInfo(){
System.out.println("共30人!!");
}
}
class ClassLeader {
private Clazz c;
private void getClazz(){
this.c = new Clazz();
}
public void getClassInfo(){
this.getClazz();
this.c.getInfo();
}
}
class Teacher {
private ClassLeader cl;
private void getClassLeader(){
this.cl = new ClassLeader();
}
public void getClassInfo(){
this.getClassLeader();
this.cl.getClassInfo();
}
}
public class MyClass {
public static void main(String args[]) {
Teacher t = new Teacher();
t.getClassInfo();
}
}
output
共30人!!
就算是朋友,也不可能什麼都知道。一個公開類別的public屬性或方法越多,修改時的風險也會跟著提高,所以在系統在設計時應該反覆衡量以下幾點:
1. 是否還可以再减少public方法和屬性,
2. 是否可以修改為private、package、protected等權限,
3. 是否可以加上final關鍵字。
可以用安裝java sdk來舉例,一般安裝要先下載JDK,然後設定環境變數,最後確認是否安裝。可以這樣呈現:
import java.util.Random;
class JavaJDK {
private Random rand = new Random(System.currentTimeMillis());
public int downloadJDK(){
System.out.println("下載JAVA JDK");
return rand.nextInt(10); // 先用隨機數字的狀態表示狀態
}
public int setEnv(){
System.out.println("設定JAVA環境變數");
return rand.nextInt(10);
}
public int checkVer(){
System.out.println("確認JAVA版本");
return rand.nextInt(10);
}
}
class InstallSoftware {
public void installJava(){
JavaJDK j = new JavaJDK();
int jdkStatus = j.downloadJDK();
if (jdkStatus != 0){
int envStatus = j.setEnv();
if (envStatus != 0){
int verStatus = j.checkVer();
if (verStatus != 0){
System.out.println("安裝完成!!");
}
}
}
}
}
public class MyComputer {
public static void main(String args[]) {
InstallSoftware i = new InstallSoftware();
i.installJava();
}
}
output
下載JAVA JDK
設定JAVA環境變數
確認JAVA版本
安裝完成!!
這程式跑起來很正常,卻也存在著一些問題。JavaJDK類別開放太多的public方法給InstallJava了。若今天回傳的int型別要變成boolean,也要修改InstallJava,這就會有一些風險產生。所以我們需要優化一下程式:
import java.util.Random;
class JavaJDK {
private Random rand = new Random(System.currentTimeMillis());
private int downloadJDK(){
System.out.println("下載JAVA JDK");
return rand.nextInt(10); // 先用隨機數字的狀態表示狀態
}
private int setEnv(){
System.out.println("設定JAVA環境變數");
return rand.nextInt(10);
}
public int checkVer(){
System.out.println("確認JAVA版本");
return rand.nextInt(10);
}
public void install(){
int jdkStatus = this.downloadJDK();
if (jdkStatus != 0){
int envStatus = this.setEnv();
if (envStatus != 0){
int verStatus = this.checkVer();
if (verStatus != 0){
System.out.println("安裝完成!!");
}
}
}
}
}
class InstallSoftware {
public void installJava(){
JavaJDK j = new JavaJDK();
j.install();
}
}
public class MyComputer{
public static void main(String args[]) {
InstallSoftware i = new InstallSoftware();
i.installJava();
}
}
output
下載JAVA JDK
設定JAVA環境變數
確認JAVA版本
安裝完成!!
現在JavaJDK類別內的安裝的步驟downloadJDK和setEnv變成private,開放了一個install()供外部呼叫。如此一來即使要修改JAVA類別內安裝的步驟或邏輯,也不會影響到其他類別。
一個方法,放在本類別沒問題,但放在其他類別也不會出錯。checkVer
import java.util.Random;
class JavaJDK {
private Random rand = new Random(System.currentTimeMillis());
private int downloadJDK(){
System.out.println("下載JAVA JDK");
return rand.nextInt(10); // 先用隨機數字的狀態表示狀態
}
private int setEnv(){
System.out.println("設定JAVA環境變數");
return rand.nextInt(10);
}
public int checkVer(){
System.out.println("確認JAVA版本");
return rand.nextInt(10);
}
public void install(){
int jdkStatus = this.downloadJDK();
if (jdkStatus != 0){
int envStatus = this.setEnv();
if (envStatus != 0){
int verStatus = this.checkVer();
if (verStatus != 0){
System.out.println("安裝完成!!");
}
}
}
}
public void isInstalledJava(){
int verStatus = this.checkVer();
if (verStatus != 0){
System.out.println("Java已安裝!!");
} else {
System.out.println("Java未完成!!");
}
}
}
class InstallSoftware {
JavaJDK j = new JavaJDK();
public void installJava(){
this.j.install();
}
public void isInstalledJava(){
int verStatus = this.j.checkVer();
if (verStatus != 0){
System.out.println("Java已安裝!!");
} else {
System.out.println("Java未完成!!");
}
}
}
public class MyComputer{
public static void main(String args[]) {
InstallSoftware i = new InstallSoftware();
i.isInstalledJava();
}
}
將第二點的範例做了一下更改,發現isInstalledJava()放在InstallSoftware類別內或是JavaJDK類別內都不會有問題。所以這個原則的意思就是:如果一個方法放在本類中,不增加類別間關係,也不對本類產生負面影響,那就可以放到本類中。
若使用不當,未來修改屬性,序列化時會拋異常NotSerializableException。
詳細可以參考這篇**謹慎實現Serializable介面**
看完了有關LoD的解釋,這時來舉個例子讓我們更熟悉這個法則。
六度人脈空間這個名詞大家應該很熟悉,意指你和任何一個陌生人之間,只有六個人的距離。也就是說如果我想聯繫比爾蓋茲,我只要找到我與他之間的六個人,然後藉由這六個人的介紹,我就可以順利的聯繫到比爾蓋茲!!
這跟LoD有什麼關係?假設有一件事情我沒辦法獨力完成,我需要別人的幫助,而我周遭的朋友也沒有這個能力幫助我。但其中一個朋友認識比爾蓋茲,他剛好可以幫忙解決這件事。於是我就藉由我朋友中間牽的線,讓比爾蓋茲幫助我完成這件事。
class Me {
private String name = "Me";
public MyFriend getMyFriend() {
return new MyFriend();
}
public void work() {
MyFriend mf = getMyFriend();
mf.getBillGates().work(name);
}
}
class MyFriend {
public BillGates getBillGates() {
System.out.println("MyFriend介紹BillGates!!");
return new BillGates();
}
}
class BillGates {
public void work(String name) {
System.out.println("BillGates跟" + name + "握手!!");
}
}
public class MyWorke {
public static void main(String args[]) {
Me me = new Me();
me.work();
}
}
output
MyFriend介紹BillGates!!
BillGates跟Me握手!!
最後比爾蓋茲確實幫我完成了一件事。但在class Me內,卻直接呼叫BillGates的work方法,這並不符合LoD。應該藉由MyFriend這個類別去呼叫BillGates做事才對。所以我們需要做一些更動
class Me {
private String name = "Me";
public MyFriend getMyFriend() {
return new MyFriend();
}
public void work() {
MyFriend mf = getMyFriend();
mf.work(name);
}
}
class MyFriend {
public BillGates getBillGates() {
return new BillGates();
}
public void work(String name) {
BillGates bg = getBillGates();
System.out.println("MyFriend介紹BillGates!!");
bg.work(name);
}
}
class BillGates {
public void work(String name) {
System.out.println("BillGates跟" + name + "握手!!");
}
}
public class MyWorke {
public static void main(String args[]) {
Me me = new Me();
me.work();
}
}
output
MyFriend介紹BillGates!!
BillGates跟Me握手!!
經過修改後,我與比爾蓋茲沒有直接的關係,只需要跟MyFriend溝通,請他跟蓋茲牽線,就可以完成事情。
迪米特法則核心觀念就是解耦、弱耦合,只有弱耦合後,類別的複用率才可以提高。但解耦和也是要有限度的,過度的解耦會造成系統的複雜度提高,維護困難。
各單元對其他單元所知應當有限:只瞭解與目前單元最相關之單元
解耦、弱耦合,提升類別複用率
優點
1. 類別間的耦合度低
2. 提高模組獨立性
3. 提高類別的重複使用頻率及系統的擴張性
缺點
1. 產生大量的中介類別,系統複雜性提升
範例1:只和朋友交流
範例2:朋友間也應該有距離1
範例3:朋友間也應該有距離2
範例4:是自己的就是自己的
範例5:未使用LoD
範例6:使用LoD
軟體設計原則(六)迪米特法則 -Law of Demeter
设计模式六大原则 - 迪米特法则
迪米特法则
設計模式6大原則(5):迪米特法則